package com.falko.android.raven;

import java.io.File;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

public class ImageCropActivity extends Activity {

	private final String TAG = getClass().getName();
	private String imagePath_ = null;
	private CropImageView imageView_ = null;
	private boolean imageLoaded_ = false;
	private float imageWidth_ = 0f;
	private float imageHeight_ = 0f;
	private float scaledImageWidth_ = 0f;
	private float scaledImageHeight_ = 0f;
	private float screenWidth_ = 0f;
	private float screenHeight_ = 0f;
	private float aspectRatio_ = 0f;
	private float maxCropWidth_ = 0f;
	private float cropWidth_ = 0f;
	private RectF cropRect_ = null;
	private float lastX_ = 0f;
	private float lastY_ = 0f;

	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_image_crop);

		((Button) findViewById(R.id.load_image))
				.setOnClickListener(new OnClickListener() {
					public void onClick(View v) {
						getImage();
					}
				});

		((Button) findViewById(R.id.save_image))
				.setOnClickListener(new OnClickListener() {
					public void onClick(View v) {
						updateSharedPrefs();
						finish();
					}
				});

		LinearLayout viewCont = (LinearLayout) findViewById(R.id.image_view_cont);
		imageView_ = new CropImageView(getBaseContext());
		ViewGroup p = (ViewGroup) imageView_.getParent();
		if (viewCont != p) {
			if (p != null) // this feels sloppy
				p.removeAllViews();
			viewCont.addView(imageView_);
		}

		DisplayMetrics dm = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(dm);
		screenWidth_ = dm.widthPixels;
		screenHeight_ = dm.heightPixels;
		aspectRatio_ = screenWidth_ / screenHeight_;

		loadSharedPrefs();
		if (imagePath_ == null) {
			getImage();
		}
		imageLoaded_ = false;
	}

	@Override
	public void onWindowFocusChanged(boolean hasFocus) {
		super.onWindowFocusChanged(hasFocus);

		if (!hasFocus || imageLoaded_)
			return;

		if (imagePath_ != null) {
			loadImage(imagePath_);
		} else {
			Toast msg = Toast.makeText(this.getBaseContext(),
					"Please select an image.", Toast.LENGTH_SHORT);
			msg.show();
		}
	}

	private void getImage() {
		Intent intent = new Intent(Intent.ACTION_PICK,
				android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
		startActivityForResult(intent, 0);
	}

	private void updateSharedPrefs() {
		SharedPreferences prefs = getSharedPreferences(
				RavenWallpaperService.SHARED_PREFS_NAME, MODE_PRIVATE);

		Editor editor = prefs.edit();
		editor.putString("IMAGE_PATH", imagePath_);
		editor.putInt("SAMPLE_SIZE", 1);
		editor.putFloat("SCALE_X", cropRect_.left / scaledImageWidth_);
		editor.putFloat("SCALE_Y", cropRect_.top / scaledImageHeight_);
		editor.commit();
	}

	private void loadSharedPrefs() {
		SharedPreferences prefs = getSharedPreferences(
				RavenWallpaperService.SHARED_PREFS_NAME, MODE_PRIVATE);
		imagePath_ = prefs.getString("IMAGE_PATH", null);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {

		super.onActivityResult(requestCode, resultCode, data);

		if (data == null)
			Log.v(TAG, "null data");
		else if (resultCode != RESULT_OK)
			Log.v(TAG, "Bad result");
		else {
			String path = getPath(data.getData());
			if (loadImage(path)) {
				imagePath_ = path;
			} else {
				Toast msg = Toast.makeText(this.getBaseContext(),
						"Error loading image.", Toast.LENGTH_SHORT);
				msg.show();
			}
		}
	}

	private String getPath(Uri data) {
		String[] filePathColumn = { MediaStore.Images.Media.DATA };

		Cursor cursor = getContentResolver().query(data, filePathColumn, null,
				null, null);
		cursor.moveToFirst();

		int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
		String filePath = cursor.getString(columnIndex);
		cursor.close();
		return filePath;
	}

	private boolean loadImage(String path) {

		Bitmap bg = null;
		BitmapFactory.Options options = new BitmapFactory.Options();
		int sampleSize = 1;
		if (new File(path).exists()) {
			while (bg == null) {
				options.inSampleSize = sampleSize;
				try {
					bg = BitmapFactory.decodeFile(path, options);
				} catch (OutOfMemoryError e) {
					bg = null;
					sampleSize++;
				} catch (Exception e) {
					return false;
				}
			}
		} else {
			return false;
		}

		View p = (View) imageView_.getParent();
		imageWidth_ = bg.getWidth();
		imageHeight_ = bg.getHeight();

		scaledImageWidth_ = p.getWidth();
		scaledImageHeight_ = ((scaledImageWidth_ / imageWidth_) * imageHeight_);
		if (scaledImageHeight_ > p.getHeight()) {
			scaledImageHeight_ = p.getHeight();
			scaledImageWidth_ = ((scaledImageHeight_ / imageHeight_) * imageWidth_);
		}

		if (scaledImageWidth_ <= 0 || scaledImageHeight_ <= 0) { //FIXME: NAO
			return false;
		}
		imageView_.setImageBitmap(Bitmap.createScaledBitmap(bg,
				(int) scaledImageWidth_, (int) scaledImageHeight_, false));
		bg.recycle();

		float maxX = scaledImageHeight_ * aspectRatio_;
		maxCropWidth_ = maxX > scaledImageWidth_ ? scaledImageWidth_ : maxX;
		cropWidth_ = maxCropWidth_ / 2;
		imageLoaded_ = true;
		imageView_.reset();
		return true;
	}

	private class CropImageView extends ImageView {

		private Paint cropBorderPaint_ = null;
		private Paint cropBackgroundPaint_ = null;
		private float dispX_ = 0f;
		private float dispY_ = 0f;
		private float lastZoomDistance_ = -1f;
		private int zoomPtrId_ = -1;
		private int movePtrId_ = -1;
		private boolean doZoom_ = false;
		private boolean doMove_ = false;

		public CropImageView(Context context) {
			super(context);
			setScaleType(ImageView.ScaleType.FIT_CENTER);
			cropBorderPaint_ = new Paint();
			cropBackgroundPaint_ = new Paint();
			cropRect_ = new RectF();
			cropBorderPaint_.setColor(Color.DKGRAY);
			cropBorderPaint_.setStrokeWidth(5f);
			cropBorderPaint_.setStyle(Paint.Style.STROKE);
			cropBackgroundPaint_.setColor(Color.argb(200, 0, 0, 0));

		}
		
		public void reset() {
			lastX_ = 0f;
			lastY_ = 0f;
			updateCropBounds();
		}

		public void draw(Canvas canvas) {
			super.draw(canvas);

			if (!imageLoaded_) {
				return;
			}
			canvas.drawRect(0, 0, getWidth(), cropRect_.top,
					cropBackgroundPaint_);
			canvas.drawRect(0, cropRect_.top, cropRect_.left, getHeight(),
					cropBackgroundPaint_);
			canvas.drawRect(cropRect_.right, cropRect_.top, getWidth(),
					getHeight(), cropBackgroundPaint_);
			canvas.drawRect(cropRect_.left, cropRect_.bottom, cropRect_.right,
					getHeight(), cropBackgroundPaint_);
			canvas.drawRect(cropRect_, cropBorderPaint_);
			// canvas.drawRect(0, 0, getWidth(), getHeight(), cropBorderPaint_);

		}

		public boolean onTouchEvent(MotionEvent event) {

			if (!imageLoaded_) {
				return true;
			}

			int action = event.getAction();

			switch (action & MotionEvent.ACTION_MASK) {

			case MotionEvent.ACTION_DOWN: {
				final float x = event.getX();
				final float y = event.getY();

				if (cropRect_.contains(x, y)) {

					dispX_ = lastX_ - x;
					dispY_ = lastY_ - y;
					doMove_ = true;
				}
				movePtrId_ = event.getPointerId(action
						& MotionEvent.ACTION_POINTER_INDEX_MASK);

				break;
			}

			case MotionEvent.ACTION_POINTER_DOWN: {
				if (zoomPtrId_ == -1) {
					zoomPtrId_ = event
							.getPointerId((action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
					doZoom_ = true;
					doMove_ = false;
				}
				break;
			}

			case MotionEvent.ACTION_MOVE: {

				final int movePtrIndex = event.findPointerIndex(movePtrId_);
				if (movePtrIndex == -1)
					break;
				final float x1 = event.getX(movePtrIndex);
				final float y1 = event.getY(movePtrIndex);

				if (doZoom_) {

					final int zoomPtrIndex = event.findPointerIndex(zoomPtrId_);
					if (zoomPtrIndex == -1)
						break;
					final float x2 = event.getX(zoomPtrIndex);
					final float y2 = event.getY(zoomPtrIndex);

					float distance = FloatMath.sqrt((float) (Math.pow(x2 - x1,
							2) + Math.pow(y2 - y1, 2)));

					if (lastZoomDistance_ > 0) {
						if (distance > lastZoomDistance_) {
							cropWidth_ += cropWidth_ < maxCropWidth_ ? 3 : 0;
							cropWidth_ = cropWidth_ > maxCropWidth_ ? maxCropWidth_
									: cropWidth_;
						} else {
							cropWidth_ -= cropWidth_ > 0 ? 2 : 0;
							cropWidth_ = cropWidth_ < maxCropWidth_ / 4 ? maxCropWidth_ / 4
									: cropWidth_;
						}
					}
					lastZoomDistance_ = distance;

				} else if (doMove_) {
					lastX_ = x1 + dispX_;
					lastY_ = y1 + dispY_;
				}

				updateCropBounds();
				break;
			}

			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_POINTER_UP: {
				final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
				final int pointerId = event.getPointerId(pointerIndex);
				if (pointerId == zoomPtrId_) {
					doZoom_ = false;
					lastZoomDistance_ = -1;
					zoomPtrId_ = -1;
				} else if (pointerId == movePtrId_) {
					movePtrId_ = -1;
					doMove_ = false;
				}
				break;
			}

			default: {
				break;
			}
			}

			return true;
		}

		public void updateCropBounds() {

			if (!imageLoaded_) {
				return;
			}

			float height = cropWidth_ / aspectRatio_;

			cropRect_.top = lastY_ - height / 2;
			cropRect_.left = lastX_ - cropWidth_ / 2;
			cropRect_.bottom = (lastY_ - height / 2) + height;
			cropRect_.right = (lastX_ - cropWidth_ / 2) + cropWidth_;

			float adjustX = cropRect_.left;
			float adjustY = cropRect_.top;
			float leftBound = Math.abs((scaledImageWidth_ - getWidth()) / 2);
			float rightBound = Math.abs(getWidth() - leftBound);
			float upperBound = Math.abs((scaledImageHeight_ - getHeight()) / 2);
			float lowerBound = Math.abs(getHeight() - upperBound);

			if (cropRect_.top < upperBound) {
				adjustY = upperBound;
			} else if (cropRect_.bottom > lowerBound) {
				adjustY -= cropRect_.bottom - lowerBound;
			}

			if (cropRect_.left < leftBound) {
				adjustX = leftBound;
			} else if (cropRect_.right > rightBound) {
				adjustX -= cropRect_.right - rightBound;
			}

			cropRect_.offsetTo(adjustX, adjustY);
			lastY_ = cropRect_.centerY();
			lastX_ = cropRect_.centerX();
			invalidate();

		}
	}
}
